//
//  RickyRefreshView.m
//  Ricky_RefreshControl
//
//  Created by MR-zhang on 15/7/2.
//  Copyright (c) 2015年 Ricky. All rights reserved.
//

#import "RickyRefreshView.h"

static NSString *RickyRefreshContentOffsetKeyPath = @"contentOffset";
static NSString *RickyRefreshContentInsetKeyPath  = @"contentInset";
static NSString *RickyRefreshContentSizeKeyPath   = @"contentSize";

static const CGFloat RickyRefreshHeaderViewHeight = 54.0f;// 头部高度
static const CGFloat RickyRefreshFooterViewHeight = 50.0f;// 底部高度

#define UIViewAnimation(time) \
[UIView animateWithDuration:time animations:^

@interface RickyRefreshView ()
{
    UIScrollView *_scrollView;
    RickyRefreshViewType _refreshViewType;
    RickyRefreshState _refreshState;
    UIEdgeInsets _originalContentInsets;
}
@end

@interface RickyRefreshView (Observer)
/** 添加监听 */
- (void)addObservers;
/** 移除监听 */
- (void)removeObservers;
@end

@interface RickyRefreshView (Layout)
- (void)layout;
@end

#pragma mark -- implementation
#pragma mark -- RickyRefreshView
@implementation RickyRefreshView

- (id)init
{
    self = [super init];
    if (self) {
        self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        self.backgroundColor = [UIColor clearColor];
    }
    
    return self;
}

- (id)initWithRefreshViewType:(RickyRefreshViewType)viewType
{
    if (self = [self init]) {
        self.refreshViewType = viewType;
    }
    
    return self;
}

- (void)willMoveToSuperview:(UIView *)newSuperview
{
    [super willMoveToSuperview:newSuperview];
    
    if (!newSuperview || ![newSuperview isKindOfClass:[UIScrollView class]]) {
        return;
    }
    
    _scrollView = (UIScrollView *)newSuperview;
    
    // 延迟后拿到更正之后的_scrollView.contentInset.
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)0.01*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        _originalContentInsets = _scrollView.contentInset;
    });
    
    // adjust the frame of header or footer
    CGRect newRect = self.frame;
    newRect.origin.x = 0;
    newRect.size.width = _scrollView.frame.size.width;
    self.frame = newRect;
    
    self.scrollView.alwaysBounceVertical = YES;
    
    if (self.superview) {
        [self removeObservers];
    }
    
    [self addObservers];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self layout];
}

- (void)removeFromSuperview
{
    [self removeObservers];
    [super removeFromSuperview];
}

#pragma mark -- setter
- (void)setObserversEnabled:(BOOL)observersEnabled
{
    _observersEnabled = observersEnabled;
    if (_observersEnabled) {
        [self addObservers];
    } else {
        [self removeObservers];
    }
}

- (void)setRefreshViewType:(RickyRefreshViewType)refreshViewType
{
    _refreshViewType = refreshViewType;
    [self setNeedsLayout];
}

- (void)setRefreshState:(RickyRefreshState)refreshState withCallBack:(void(^)())action
{
    if (self.refreshState == refreshState) {
        return;
    }
    
    self.refreshState = refreshState;
    
    if (_refreshState == RickyRefreshStateRefreshing) {
        if (_refreshViewType == RickyRefreshViewTypeHeader) {
            UIEdgeInsets edgeInset = _scrollView.contentInset;
            
            edgeInset.top = self.frame.size.height + _originalContentInsets.top;
            UIViewAnimation(RickyAnimationDuration) {
                _scrollView.contentInset = edgeInset;
                self.rebounded = NO;
            } completion:^(BOOL finished) {
                if (finished) {
                    self.rebounded = finished;
                    if (action) {
                        action();
                    }
                };
            }];
            
        } else if (_refreshViewType == RickyRefreshViewTypeFooter) {
            UIEdgeInsets edgeInset = _scrollView.contentInset;
            edgeInset.bottom = self.frame.size.height+_originalContentInsets.bottom+MAX(_scrollView.frame.size.height-_originalContentInsets.top-_originalContentInsets.bottom-_scrollView.contentSize.height, 0);
            UIViewAnimation(RickyAnimationDuration) {
                _scrollView.contentInset = edgeInset;
                self.rebounded = NO;
            } completion:^(BOOL finished) {
                if (finished) {
                    self.rebounded = finished;
                    if (action) {
                        action();
                    }
                }
            }];
        }
        
        if (self.actionBlock) {
            self.actionBlock();
        }
        
    } else if (_refreshState == RickyRefreshStateNormal) {
        
        // Set _scrollView 's contentInset
        if (_refreshViewType == RickyRefreshViewTypeHeader) {
            UIEdgeInsets edgeInset = _scrollView.contentInset;
            
            // Fix inset issue, when scroll view is refreshing, and push another view controller.
            edgeInset.top -= self.frame.size.height;// _originalContentInsets.top;
            
            UIViewAnimation(RickyAnimationDuration) {
                _scrollView.contentInset = edgeInset;
                self.rebounded = NO;
                
            } completion:^(BOOL finished) {
                if (finished) {
                    self.rebounded = finished;
                    if (action) {
                        action();
                    }
                }
            }];
        } else if (_refreshViewType == RickyRefreshViewTypeFooter) {
            UIEdgeInsets edgeInset = _scrollView.contentInset;
            // Fis inset issue, when scroll view is refreshing, and push another view controller.
            edgeInset.bottom -= self.frame.size.height;//_originalContentInsets.bottom.
            
            // if Scroll view content size height less than it's frame.
            //                        edgeInset.bottom -= MAX(_scrollView.frame.size.height-_originalContentInsets.top-_originalContentInsets.bottom-_scrollView.contentSize.height, 0);
            
            UIViewAnimation(RickyAnimationDuration) {
                _scrollView.contentInset = edgeInset;
                self.rebounded = NO;
            } completion:^(BOOL finished) {
                if (finished) {
                    self.rebounded = finished;
                    if (action) {
                        action();
                    }
                }
            }];
        }
    }
}

- (void)setCompleteType:(RickyRefreshCompleteType)completeType
{
    _completeType = completeType;
}

@end

#pragma mark -- RickyRefreshView (Observer)
@implementation RickyRefreshView (Observer)

- (void)addObservers
{
    [_scrollView addObserver:self forKeyPath:RickyRefreshContentOffsetKeyPath options:NSKeyValueObservingOptionNew context:nil];
    [_scrollView addObserver:self forKeyPath:RickyRefreshContentInsetKeyPath options:NSKeyValueObservingOptionNew context:nil];
    [_scrollView addObserver:self forKeyPath:RickyRefreshContentSizeKeyPath options:NSKeyValueObservingOptionNew context:nil];
}

- (void)removeObservers
{
    [self.superview removeObserver:self forKeyPath:RickyRefreshContentOffsetKeyPath];
    [self.superview removeObserver:self forKeyPath:RickyRefreshContentInsetKeyPath];
    [self.superview removeObserver:self forKeyPath:RickyRefreshContentSizeKeyPath];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:RickyRefreshContentOffsetKeyPath]) {
        [self contentOffsetChanged:change];
    } else if ([keyPath isEqualToString:RickyRefreshContentInsetKeyPath]) {
        [self contentInsetChanged:change];
    } else if ([keyPath isEqualToString:RickyRefreshContentSizeKeyPath]) {
        [self contentSizeChange:change];
    }
}

- (void)contentOffsetChanged:(NSDictionary *)change
{
    if (_refreshState == RickyRefreshStateRefreshing) {
        return;
    }
    
    // Correct content inset firstly set here, assignment in willMoveToSuperView no work infact
    // _originalContentInsets = _scrollView.contentInset;
    CGPoint contentOffset = _scrollView.contentOffset;
    if (_scrollView.isDragging) {
        CGFloat ratio = 0;
        if (_refreshViewType==RickyRefreshViewTypeHeader && !self.hidden) {
            CGFloat offsetY = contentOffset.y+_originalContentInsets.top;
            if (offsetY <= -self.frame.size.height) {
                _refreshState = RickyRefreshStateReady;
            } else {
                _refreshState = RickyRefreshStateNormal;
            }
            
            if (offsetY < 0) {
                ratio = MAX(0, MIN(-offsetY/self.frame.size.height, 1.5)) - 0.5;
                self.refreshRatio = ratio;
            }
        } else if (_refreshViewType==RickyRefreshViewTypeFooter  && !self.hidden) {
            CGFloat offsetY = contentOffset.y+_originalContentInsets.top;
            CGFloat beginY = MAX(_scrollView.contentSize.height-_scrollView.frame.size.height+_originalContentInsets.top+_originalContentInsets.bottom, 0);
            if (offsetY >= beginY+self.frame.size.height) {
                _refreshState = RickyRefreshStateReady;
            } else {
                _refreshState = RickyRefreshStateNormal;
            }
            if (offsetY > beginY) {
                ratio = MAX(0, MIN((offsetY-beginY)/self.frame.size.height, 1.5)) - 0.5;
                self.refreshRatio = ratio;
            }
        }
    } else {
        if (_refreshViewType==RickyRefreshViewTypeHeader  && !self.hidden) {
            if (_refreshState == RickyRefreshStateReady) {
                [self setRefreshState:RickyRefreshStateRefreshing withCallBack:NULL];
            }
        } else if (_refreshViewType==RickyRefreshViewTypeFooter  && !self.hidden) {
            if (_refreshState == RickyRefreshStateReady) {
                [self setRefreshState:RickyRefreshStateRefreshing withCallBack:NULL];
            }
        }
    }
}

- (void)contentInsetChanged:(NSDictionary *)change
{
    [self layout];
}

- (void)contentSizeChange:(NSDictionary *)change
{
    [self layout];
}

@end

#pragma mark -- RickyRefreshView (Layout)
@implementation RickyRefreshView (Layout)

- (void)layout
{
    CGRect newRect = self.frame;
    switch (_refreshViewType) {
        case RickyRefreshViewTypeHeader:
        {
            newRect.size.height = RickyRefreshHeaderViewHeight;
            newRect.origin.y = -newRect.size.height;
        }
            break;
        case RickyRefreshViewTypeFooter:
        {
            newRect.size.height = RickyRefreshFooterViewHeight;
            newRect.origin.y = MAX(_scrollView.contentSize.height, _scrollView.frame.size.height-_originalContentInsets.top-_originalContentInsets.bottom);
        }
            break;
    }
    
    self.frame = newRect;
}

@end
